package fregat.usb.configprovider;

import java.io.FileNotFoundException;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;

import javax.swing.text.html.ListView;

import fregat.base.util.ICloseable;
import fregat.base.util.Logger;
import fregat.base.util.XMLFilePath;
import fregat.base.database.DBLink;
import fregat.base.database.DatabaseConnector;
import fregat.base.database.ServiceName;
import fregat.base.database.Sid;
import fregat.base.database.DBLink.ConfigurationInvalidException;


public class DBConfigProvider implements IConfigProvider, IConfigPublisher, ICloseable {
	
	private Configuration configuration = null;
	
	private DatabaseConnector connector;
	private ArrayList<IConfigListener> listeners;
	private DBLink link;
	private DBAlertListener alertListener = null;
	
	
	Logger log;
	XMLFilePath configfilepath;
	
	public DBConfigProvider () throws FileNotFoundException {
		configfilepath = new XMLFilePath("dbconfig.xml");
		log = Logger.getCategory("usb");
		link = new DBLink(configfilepath);
		listeners = new ArrayList<IConfigListener>();
	}
	
	public DBConfigProvider (String path) throws FileNotFoundException {
		configfilepath = new XMLFilePath(path);
		log = Logger.getCategory("usb");
		link = new DBLink(configfilepath);
		listeners = new ArrayList<IConfigListener>();
	}
	
	@Override
	public IConfiguration getConfiguration () {
		try {
			if (connector == null) {
				link.readDBLink();
				String host = link.get(DBLink.DBConfigAttributes.DBSERVER);
				String service_name = link.get(DBLink.DBConfigAttributes.DBINSTANCE);
				String port = link.get(DBLink.DBConfigAttributes.DBPORT);
				String name = link.get(DBLink.DBConfigAttributes.DBNAME);
				String user = link.get(DBLink.DBConfigAttributes.DBUSER);
				String password = link.get(DBLink.DBConfigAttributes.DBPASS);
				if (service_name != null)
					connector = new DatabaseConnector(host, user, password, new ServiceName(service_name), Integer.parseInt(port), log);
				else if (name != null)
					connector = new DatabaseConnector(host, user, password, new Sid(name), Integer.parseInt(port), log);
				connector.connectToDatabase(host, user, password);
			}
			
			configuration = prepareConfiguration ();
		}
		catch (ConfigurationInvalidException ciex) {
			log.error("", ciex);
			configuration = null;
		}
		catch (DBConfigProvider.DatabaseIsNotConnected dbncex) {
			log.error("", dbncex);
		}
		return configuration;
	}
	
	private Configuration prepareConfiguration () throws DatabaseIsNotConnected {
		if (configuration == null) {
			configuration = new Configuration ();
			if (connector == null)
				throw new DatabaseIsNotConnected ();
			CallableStatement s;
			try {
				s = connector.prepareStatement("INIT", "select key, statement from sql where lower(type) = ?");
				s.setString(1, "config");
				ResultSet rs = connector.executeQuery("INIT");
				while (rs.next()) {
					String key = rs.getString(1);
					String statement = rs.getString(2);
					connector.prepareStatement(key, statement);
				}
			} catch (SQLException sqlex) {
				// TODO Auto-generated catch block
				sqlex.printStackTrace();
			}
			
		}
		CallableStatement vendor_statement = connector.getStatement("vendor");
		CallableStatement product_statement = connector.getStatement("product");
		try {
			vendor_statement.setString(1, "pic18f4550");
			product_statement.setString(1, "pic18f4550");
			ResultSet rs_vendor = vendor_statement.executeQuery();
			ResultSet rs_product = product_statement.executeQuery();
			rs_vendor.next();
			rs_product.next();
			String vendor = rs_vendor.getString(1);
			String product = rs_product.getString(1);
			configuration.setProductId(Integer.parseInt(product));
			configuration.setVendorId(Integer.parseInt(vendor));
		} catch (SQLException sqlex) {
			sqlex.printStackTrace();
		}
		return configuration;
	}
	
	public void startSession () {
		getConfiguration();
		alertListener = new DBAlertListener();
		alertListener.start();
	}
	
	
	public void close () throws ICloseable.CloseException {
		if (connector == null || ! connector.isConnected() )
			return;
		alertListener.shutdown();
		try {
			alertListener.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		connector.shutdown();
		if (connector.isConnected())
			throw new CloseException();
		return;
	}
	
	public boolean isALive () {
		return !alertListener.shutdown;
	}
	
	public void waitForJoin () {
		try {
			alertListener.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return;
	}
	
	public void endSession () {
		try {
			close ();
		} catch (CloseException e) {
			e.printStackTrace();
		}
	}
	
	public void registerListener (IConfigListener cl) {
		listeners.add(cl);
	}
	
	public class DatabaseIsNotConnected extends Exception {
		private static final long serialVersionUID = 1L;
	}
	
	
	public class DBAlertListener extends Thread {
		public boolean shutdown;
		
		public void shutdown () {
			shutdown = true;
		}
		public void run () {
			shutdown = false;
			
			while (!shutdown) {
				connector.waitForAlert("CONFIG", 5000);
				IConfiguration c = getConfiguration();
				Iterator<IConfigListener> it = listeners.iterator();
				while (it.hasNext()) {
					it.next().configChanged(c);
				}
			}
		}
	}

}
